package com.inspur.security.cbb3.kms.keymanager.keyorders.service.impl;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.inspur.security.cbb3.kms.enums.*;
import com.inspur.security.cbb3.kms.exception.UserInfoInVaildException;
import com.inspur.security.cbb3.kms.keks.service.KeksService;
import com.inspur.security.cbb3.kms.keks.vo.CipherTextVo;
import com.inspur.security.cbb3.kms.keymanager.keyorders.po.KeyOrdersBean;
import com.inspur.security.cbb3.kms.keymanager.keyorders.repository.KeyOrdersRepository;
import com.inspur.security.cbb3.kms.keymanager.keyorders.service.KeyOrdersService;
import com.inspur.security.cbb3.kms.keymanager.keyorders.vo.KeyOrdersAddVO;
import com.inspur.security.cbb3.kms.keymanager.secrets.repository.SecretsRepository;
import com.inspur.security.cbb3.kms.keymanager.secrets.service.SecretsService;
import com.inspur.security.cbb3.kms.sysuser.po.SysUserBean;
import com.inspur.security.cbb3.kms.sysuser.service.SysUserService;
import com.inspur.security.cbb3.kms.utils.DateUtil;
import com.inspur.security.cbb3.kms.utils.PageRequestDate;
import com.inspur.security.cbb3.kms.utils.RegExpUtil;
import javassist.NotFoundException;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.security.web.firewall.RequestRejectedException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.MissingServletRequestParameterException;

import javax.persistence.criteria.Predicate;
import javax.validation.ValidationException;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;

/**
 * 密钥订单service
 *
 * @author lijunchang
 */
@Service
@Slf4j
@Transactional(readOnly = true)
public class KeyOrdersServiceImpl implements KeyOrdersService {

    /**
     * 密钥订单密码长度最小为6位，最大为20位
     */
    private static final int PASSWORD_MIN_LENGTH = 6;
    private static final int PASSWORD_MAX_LENGTH = 20;

    @Autowired
    private KeyOrdersRepository keyOrdersRepository;

    @Autowired
    private SysUserService sysUserService;

    @Autowired
    private SecretsService secretsService;

    @Autowired
    private SecretsRepository secretsRepository;

    @Autowired
    private KeksService keksService;



    /**
     * 进行密钥订单新增
     * 0.参数校验
     * 1.插入到密钥订单表
     * 2.rabbitmq调用密钥生成方法
     *
     * @param keyOrdersAddVO 密钥订单类
     * @return 新增结果
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public KeyOrdersBean generateKeyOrder(KeyOrdersAddVO keyOrdersAddVO) {
        SysUserBean loginUser = sysUserService.findLoginSysUser();
        if (loginUser == null) {
            throw new UserInfoInVaildException();
        }

        this.checkInput(keyOrdersAddVO, loginUser.getUuid());

        ObjectMapper mapper = new ObjectMapper();
        KeyOrdersBean keyOrdersBean = new KeyOrdersBean();

        keyOrdersBean.setCreatorId(loginUser.getUuid());
        keyOrdersBean.setDeleted(false);
        keyOrdersBean.setCreatedTime(DateUtil.getNowDate());
        keyOrdersBean.setStatus(KeyOrderStatus.Pending.name());
        keyOrdersBean.setType(keyOrdersAddVO.getType());
        keyOrdersBean.setName(keyOrdersAddVO.getName());

        //用用户的二级密钥对传入密码进行加密
        if (StringUtils.isNotBlank(keyOrdersAddVO.getPassword())) {
            //CipherTextVo getEncryptString(String plainText, String creatorId);
            CipherTextVo cipherTextVo = keksService.getEncryptString(keyOrdersAddVO.getPassword(), loginUser.getUuid());
            if (cipherTextVo == null) {
                throw new RequestRejectedException("二级密钥加密传入密码错误");
            }
            keyOrdersAddVO.setPassword(cipherTextVo.getCipherText());
            keyOrdersAddVO.setKekId(cipherTextVo.getKekId());
        }

        try {
            keyOrdersBean.setMeta(mapper.writeValueAsString(keyOrdersAddVO));
        } catch (JsonProcessingException e) {
            log.error("generateKeyOrder setMeta errpr", e);
            throw new RequestRejectedException("密钥订单新增异常，错误原因：对象转换出错。");
        }
        return keyOrdersRepository.save(keyOrdersBean);
    }

    /**
     * 进行订单新增数据校验，不合规会抛出ValidationException异常
     *
     * @param keyOrdersAddVO 订单新增数据
     * @param loginUserUuid  操作用户，用来校验当前用户下是否已存在相同名称的密钥
     */
    private void checkInput(KeyOrdersAddVO keyOrdersAddVO, String loginUserUuid) {
        if (!RegExpUtil.checkSecretOrderAlgorithm(keyOrdersAddVO.getType(), keyOrdersAddVO.getAlgorithm(), keyOrdersAddVO.getBitLength())) {
            throw new ValidationException("传入密钥算法不规范，校验失败");
        }
        if(!RegExpUtil.checkKeyExpiry(keyOrdersAddVO.getExpiration())){
            throw new ValidationException("传入密钥有效期不规范，请传入比当前日期大的日期，格式为'yyyy-MM-dd'");
        }
        String pwd = keyOrdersAddVO.getPassword();
        //密码长度校验，如果是对称加密，不需要输入密码
        boolean pwdRes = StringUtils.isNotBlank(pwd) && (pwd.length() > PASSWORD_MAX_LENGTH || pwd.length() < PASSWORD_MIN_LENGTH);
        //进行密钥格式、输出格式校验
        //如果是对称加密，输出格式只能为hex/base64,密钥格式为空
        //如果是非对称加密，密钥格式为pkcs8/pkcs1、输出格式为空，如果传入密钥格式为pkcs1，则不能输入密码
        switch (AlgorithmType.getByType(keyOrdersAddVO.getAlgorithm())) {
            case aes:
            case desede:
            case sm4:
                if (StringUtils.isBlank(keyOrdersAddVO.getOutputFormat()) ||
                        DataOutputType.getByType(keyOrdersAddVO.getOutputFormat()).equals(DataOutputType.Other)
                ) {
                    throw new ValidationException("传入输出格式不规范，校验失败");
                }
                keyOrdersAddVO.setPassword(null);
                keyOrdersAddVO.setKeyFormat(null);
                break;
            case rsa:
            case dsa:
                if (StringUtils.isBlank(keyOrdersAddVO.getKeyFormat()) ||
                        AsymmetricCommonPrivateKeyFormatType.getByType(keyOrdersAddVO.getKeyFormat()).equals(AsymmetricCommonPrivateKeyFormatType.Other)
                ) {
                    throw new ValidationException("传入密钥格式不规范，校验失败");
                    //密码长度校验
                } else if (pwdRes) {
                    throw new ValidationException(String.format("传入密码长度应为%d~%d", PASSWORD_MIN_LENGTH, PASSWORD_MAX_LENGTH));
                }
                keyOrdersAddVO.setOutputFormat(null);
                break;
            case sm2:
                if (StringUtils.isBlank(keyOrdersAddVO.getKeyFormat()) ||
                        AsymmetricSM2PrivateKeyFormatType.getByType(keyOrdersAddVO.getKeyFormat()).equals(AsymmetricSM2PrivateKeyFormatType.Other)
                ) {
                    throw new ValidationException("传入密钥格式不规范，校验失败");
                } else if (pwdRes) {
                    throw new ValidationException(String.format("传入密码长度应为%d~%d", PASSWORD_MIN_LENGTH, PASSWORD_MAX_LENGTH));
                }
                keyOrdersAddVO.setOutputFormat(null);
                break;
            default:
        }

        //查询当前登录用户名下是否有相同名称的密钥
        long totalNumber = secretsRepository.countByNameAndDeletedAndCreatorIdOrderByCreatedTimeDesc(keyOrdersAddVO.getName().trim(), false,loginUserUuid);
        if (totalNumber > 0) {
            throw new ValidationException("传入密钥名称已存在");
        }
    }

    /**
     * MQ根据密钥订单uuid进行密钥生成
     * 0.查询密钥订单及其状态
     * 1.根据订单类型进行密钥操作/容器操作
     * 2.根据操作结果进行订单更新
     *
     * @param uuid 数据id
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void mqGenerateKeyOrder(String uuid) {
        secretsService.generateKey(uuid);
    }


    /**
     * 删除密钥订单
     *
     * @param uuid 数据id
     * @return 操作结果
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public KeyOrdersBean deleteKeyOrder(String uuid) throws MissingServletRequestParameterException, NotFoundException {
        KeyOrdersBean bean = checkPermission(uuid);
        bean.setDeleted(true);
        bean.setDeleteTime(DateUtil.getNowDate());
        keyOrdersRepository.save(bean);
        return bean;
    }

    /**
     * 进行密钥订单的更新
     *
     * @param keyOrdersBean 密钥订单信息
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public KeyOrdersBean update(KeyOrdersBean keyOrdersBean) {
        keyOrdersBean.setUpdatedTime(DateUtil.getNowDate());
        return keyOrdersRepository.saveAndFlush(keyOrdersBean);
    }

    /**
     * 根据id查询
     *
     * @param uuid 数据id
     * @return 查询结果
     */
    @Override
    public KeyOrdersBean searchKeyOrder(String uuid) throws MissingServletRequestParameterException, NotFoundException {
        return checkPermission(uuid);
    }

    /**
     * 分页查询
     *
     * @param pageRequestDate 分页数据
     * @param searchParams    查询条件
     * @return 查询结果集
     */
    @Override
    public Page<KeyOrdersBean> searchKeyOrders(PageRequestDate pageRequestDate, KeyOrdersBean searchParams) {
        SysUserBean loginUser = sysUserService.findLoginSysUser();
        if (loginUser == null) {
            throw new UserInfoInVaildException();
        }
        //进行分页设置
        return keyOrdersRepository.findAll((Specification<KeyOrdersBean>) (root, query, cb) -> {
            List<Predicate> list = new ArrayList<>();
            list.add(cb.equal(root.get("creatorId").as(String.class), loginUser.getUuid()));
            list.add(cb.equal(root.get("deleted").as(Boolean.class), false));
            if (StringUtils.isNotBlank(searchParams.getName())) {
                list.add(cb.like(root.get("name").as(String.class), "%" + searchParams.getName().trim() + "%"));
            }
            if (StringUtils.isNotBlank(searchParams.getStatus())) {
                list.add(cb.equal(root.get("status").as(String.class), searchParams.getStatus().trim()));
            }
            if (StringUtils.isNotBlank(searchParams.getCreatedTimeBegin())) {
                list.add(cb.greaterThanOrEqualTo(root.get("createdTime").as(String.class), searchParams.getCreatedTimeBegin().trim()));
            }
            if (StringUtils.isNotBlank(searchParams.getCreatedTimeEnd())) {
                list.add(cb.lessThanOrEqualTo(root.get("createdTime").as(String.class), searchParams.getCreatedTimeEnd().trim()));
            }
            //TODO 查询条件
            Predicate[] p = new Predicate[list.size()];
            return cb.and(list.toArray(p));
        }, pageRequestDate.getPageRequest());
    }

    /**
     * 校验登录用户是否有数据的操作权限
     *
     * @param uuid 数据id
     * @return 数据对象
     * @throws MissingServletRequestParameterException 异常信息-主键参数必填
     * @throws NotFoundException                       异常信息-未找到对象
     */
    private KeyOrdersBean checkPermission(String uuid) throws MissingServletRequestParameterException, NotFoundException {
        if (StringUtils.isBlank(uuid)) {
            throw new MissingServletRequestParameterException("主键字段uuid", "String");
        }
        SysUserBean loginUser = sysUserService.findLoginSysUser();
        if (loginUser == null) {
            throw new UserInfoInVaildException();
        }
        Optional<KeyOrdersBean> optional = keyOrdersRepository.findById(uuid);
        //校验是否存在，如果不存在，直接返回成功
        if (!optional.isPresent()) {
            throw new NotFoundException("密钥订单对象不存在");
        }
        KeyOrdersBean bean = optional.get();
        //是否有查询权限
        if (bean.getDeleted() || !loginUser.getUuid().equalsIgnoreCase(bean.getCreatorId())) {
            log.info("登录用户暂无查看该信息的权限，登录用户uuid：[{}]，密钥订单uuid：[{}]，密钥订单用户uuid：[{}]", loginUser.getUuid(), uuid, bean.getCreatorId());
            throw new NotFoundException("密钥订单对象不存在");
        }
        return bean;
    }


}
